![]() ![]() |
アップルの新しい Power Mac G5今日のクリエイティブなプロフェッショナルたちにとって、増え続ける大量のデータを素早く効率的に処理できることは、成功する上で不可欠な能力であることから、並外れた処理能力とシステムの性能が要求されています。 メモリを大量に消費するアプリケーションや大容量のメディアファイルを扱うために、膨大な量の RAM が必要になります。 また、複雑な 3D モデルを視覚化し、高解像度のイメージを操作するために、超高速で、高性能なグラフィックス機能も必要です。 さらに、業界に特化した無数の周辺機器に接続するために、広範な入出力オプションも必要とされています。 今度新しく登場した Power Mac G5(970 としても知られています)は、世界で最もパワフルなパーソナルコンピュータであり、世界中のクリエイティブなプロフェッショナルたちの、妥協を許さない高性能への要求にすぐにでも応えることができます。 この革命的なデスクトップコンピュータは、64 ビットプロセッサ、PowerPC G5 を初めて搭載し、IBM との共同開発により、同社の最先端の製造技術を利用して作られました。 また、G5 プロセッサは、ネイティブで 32 ビットコードを実行するため、Mac OS X と Classic のアプリケーションは Power Mac G5 でネイティブに動作します。 しかし、G5 固有の機能とパフォーマンスを最大限に利用するには、アプリケーションのチューニングと調整が若干必要になります。 このテクニカルノートでは、その作業にどのようにして取りかかればよいかを説明します。 アプリケーションにパフォーマンス上の決定的なホットスポットがない場合でも、いくつかのコンパイラフラグを有効にして、パフォーマンスを向上できます。 詳細については、「G5 固有のコンパイラオプション」と「一般的な最適化を使ったコンパイル」のセクションを参照してください。
|
![]() 図 1. ビルド設定の追加例 |
gcc 3.3 は、以前のバージョンの gcc よりも忠実に、C と C++ の仕様に準拠しています。 したがって、gcc 3.3 を使ってコードをコンパイルすると、アップデートが必要な何行かのコードが指摘され、細かい不具合を除去するのに役立ちます。
次に、CHUD (Computer Hardware Understanding Developer) ツールをインストールします。 これは、低レベルのハードウェア中心のパフォーマンスツールであり、Shark も含まれています。 G5 に対応した最新バージョンの CHUD ツールをダウンロードできます。 また、Xcode ツールにも含まれています。 通常、ダウンロードサイトには、CD に含まれているものよりも新しいバージョンの CHUD が用意されています。 これらのツールは、「/Developer/Applications/CHUD/」にインストールされ、低レベルのパフォーマンス分析に使用できる他のシステムコンポーネントもインストールされます。
いくつかのコンパイラフラグは、すべてのプロセッサ上で実行時のパフォーマンスが向上するように設計されており、このためすべてのプロセッサで実行するコードに対して使用できます。 特に、gcc の -O3 などのフラグ(「ターゲット」>「GCC コンパイラ設定」のポップアップメニューを通じてアクセスできます)は、アプリケーションのコードを改善する一歩として、最初に有効にする必要があります。 しかし、このフラグを有効にしたあと、G5 用のコードをチューニングするときに他の gcc 3.3 コンパイラフラグがいくつかあります。 これらのフラグは、ターゲットごとに使用したり、または特定のソースファイルに追加してそのソースファイルだけをこのオプションでコンパイルするようにできます。 どちらを選ぶかは、使用するフラグとアプリケーションの設計によって決まります。 いくつかのフラグでは、コンパイラは、新しい G5 プロセッサによってのみ解釈される命令を使用できます。 その命令を使用すると、G5 でのパフォーマンスは向上しますが、従来のプロセッサがそのような命令を実行しようとすると、アプリケーションがクラッシュする場合があります。 この問題を解決するには、主として 3 つの方法があります。
以下に、最も一般的なコンパイラフラグをいくつか示します。
-mcpu=970 | このフラグを使うとコンパイラは、G5(970 としても知られている)プロセッサでのみ利用可能な命令を使用できます。 -mcpu=G5 も使用できます。 |
-mtune=970 | このフラグは、G5 向けに可能な限り最適にコードをチューニングするようにコンパイラに指示します。 コードの互換性は変わらないため、G5 以外のプロセッサで実行されるコードに対して、単独で安全に使用できます。 -mtune=G5 も使用できます。 |
-mpowerpc64 | このフラグは、上記のフラグと組み合わせると、long long 型を扱うときにパフォーマンスを大幅に向上させるために、G5 ネイティブの 64 ビット long long 型のサポートを有効にするようにコンパイラに指示します。 |
-mpowerpc-gpopt | このフラグは、上記のフラグと組み合わせると、パフォーマンスを大幅に向上させるために、G5 のハードウェア浮動小数点平方根のサポートを有効にするようにコンパイラに指示します。 |
-force_cpusubtype_ALL | このフラグは、 上記のフラグを使って生成されたコードを「G5 専用」として認識しないようにコンパイラに指示します。 これにより、条件付きのプロセッサチェックを、 Mac OS X ではなく G3 や G4 上でランタイムで行うことができ、コードの実行を防ぐことができます。 このフラグを使う場合は、G5 専用の命令を含んでいる関数を、G3 や G4 で実行しないように注意してください。そうしないとアプリケーションがクラッシュすることがあります。 |
ターゲット全体を対象とするフラグは、「ターゲット」タブから「GCC コンパイラ設定」を開き、「その他の C コンパイラフラグ」フィールドにフラグを入力することによって、ターゲットごとに設定できます。
![]() 図 2. 「ターゲット」設定に G5 固有の最適化フラグを入力 |
ソースファイルごとのフラグは、次のようにして、ソースファイルごとに設定できます。
コンパイルするターゲットの「ターゲット」タブを開き、「ビルドフェーズ」で「ソース」を選択します。
![]() 図 3. 「ソース」ビルドフェーズの選択 |
次に、リスト内でフラグを追加したいソースファイルをクリックし、Command+I を選択して、そのファイルの「情報を見る」を表示します。 通常表示されるのとは異なる「情報」パネルが開き、ファイルごとのコンパイルフラグを入力するためのテキストフィールドが表示されます。
![]() 図 4. 「情報」パネルでのファイルごとのコンパイラフラグの入力 |
G5 固有のコードを、アプリケーションの残りの部分から簡単に切り離せる場合があります。 この場合、別のフレームワークにそのコードを入れて、アプリケーションをフレームワークにリンクすることを検討します。 そうすれば、ソースファイルごとにフラグを指定したり、アプリケーション全体に影響する設定について心配したりせずに、ターゲット全体の設定をそのフレームワークに適用できます。 実行時に G5 で実行していることがわかった場合は、フレームワークへの呼び出しを行います。
Shark は CHUD アプリケーションの 1 つであり、G5 チューニングツールキットのうち主要なツールの 1 つになるべきものです。 Shark を使って、アプリケーションのサンプリングを時間ベースで実行できます。 つまり、コンピュータの状態とともにアプリケーションを定期的に調べて、何が起きているかを調べます。 さらに、デバッグシンボルを有効にした状態でアプリケーションをコンパイル(または Project Builder の設定を有効にするかコマンドラインで -g
オプションを使用して)、そのアプリケーションに対するソースを利用可能にすると、Shark はコード中のどの行に最も時間がかかっているかを示すソースビューを表示できます。
Shark には多数のオプションがあり、ここですべてを解説することはできません(Shark には詳細なヘルプが用意されており、ヘルプメニューからアクセスできます)。 代わりに、ここでは、いくつかの典型的な使用法のパターンについて説明します。 Shark を起動すると、ほとんどの作業を行うことになるウインドウが 1 つ開きます。 「Configure」ボタンを押すことによって、多くの一般的な Shark オプションを設定できます。
よく設定するオプションとしては、Shark で取得するシステムのサンプル数と、サンプルの間隔(タイマーがトリガを発行するタイミング)の 2 つがあります。 Shark のサンプリングセッションは、いつでも手動で停止できますが、時には特定の長さの時間だけサンプリングを実施したいこともあり、このオプションでそれを実行できます。
![]() 図 5. Shark のサンプリングオプションのパネル |
アプリケーションをサンプリングするには、アプリケーションの実行を開始し、次に Shark で「Start」ボタンまたは Option+Escape ホットキーを押します。 サンプリングを停止するには、「Stop」ボタンを押すか、もう一度 Option+Escape ホットキーを押します(または、単にすべてのサンプリングが完了するのを待ちます)。 Shark は、実際には 1 つのアプリケーションだけをサンプリングするということはありません。 実際にはコンピュータ全体をサンプリングし、サンプリング終了後、デフォルトではサンプリング中に最も多く CPU 時間を使ったプロセスのサンプルデータを表示します。 このため、デフォルトでアプリケーションが表示されない場合は、サンプルセッションウインドウの一番下の「Process」ポップアップを見て、そのアプリケーションがリストのほかの場所にないかどうか確認します。
サンプリング後、アプリケーションの最初の表示は通常、呼び出された関数とメソッド、それらが含まれているバイナリ、そのプロセスの関数別の CPU 使用率が一覧で表示されます。
![]() 図 6. Shark のサンプリング結果の例 |
通常、Mac OS X のバイナリは自分の管理下にはできないため、合計時間の大部分を占め、なおかつ Mac OS X のバイナリに含まれていないルーチンが最適化の対象となります。 図 6 では、DoSomething
が、このアプリケーションの中で CPU 時間のほとんどを占めており、我々の管理下にあることがわかります。 このようなルーチンが見つかったら、ルーチンをダブルクリックしてソースビューを表示します。
![]() 図 7. サンプルのソースを使った Shark の結果の例 |
ソースビューには、選択したルーチンのソースコード(利用できる場合)が、ホットスポット(アプリケーションの CPU 時間全体のうち大部分を占めるコード行)がハイライトされた状態で示されます。 ここでは、時間はすべて(単なる例に過ぎないので、予想通り)無駄な計算を実行する短いループに費やされていることがわかります。
いくつかのコード行の横に感嘆符(とコメント)が付いています。 この感嘆符をクリックすると、テキストバルーンが現れて、Shark が最適化されていない特定のコードパターンを検出した場所と、修正のヒントが示されます。 図 8 では、for
ループのアラインメントの欠如について警告されていることがわかります(詳細については「コードのアライン」のセクションを参照してください)。
![]() 図 8. コード最適化のために Shark が提供するヒント |
ソースビューでソースコードの行をダブルクリックすると、コードのアセンブラビューが表示されます。 個々の命令が並べられ、プロセッサのストール、命令の実行に要したサイクル数などの情報が表示されます。 この情報は、ウインドウの右下隅のポップアップを使って選択された CPU モデルに基づいて、Shark によって計算されます。 デフォルトでは、選択された CPU モデルは、実行中のプロセッサと同じにしますが、「970」を選んで、G5 上でのこの情報についての Shark の最善の推定値を見ることができます。
![]() 図 9. Shark のアセンブラビュー |
アセンブラコードが黒の太線で区切られています。 アセンブリビューから G5 の情報を最大限に取得するためには、Shark の環境設定で、G5 命令グループ化の視覚表示を有効にします。 G5 は命令を集め、それをグループ単位で処理します。 いくつかの命令は、ディスパッチグループの特定のスロットに入れる必要があるため、ある命令に必要なスロットがすでにいっぱいの場合には、不完全なグループが処理に送られることがあります。 最大のパフォーマンスは、すべてのグループが完全な場合に発揮されます。 このため、Shark でグループのディスパッチ状況を見れば、どの行のコードを並べ替えたり書き換えたりすることで、より完全なグループになることを保証できるかを判断するのに役立ちます。 Shark のディスパッチグループの表示は、命令のグループ化についての Shark の最善な推定です。 実際の G5 でのアプリケーションテストに勝るものではありません。
![]() 図 10. G5 チューニングのためのグループディスパッチの有効化 |
アプリケーション内の G5 固有の命令または G5 向けに最適化されたコードを実行するのであれば、おそらく実行時にアプリケーションが G5 で実行しているかどうかを確認することになります。 適切な処理を行い、実行中のアーキテクチャ向けに正しく最適化されたルーチンを呼び出します。 これにより、G3 や G4 のような古いアーキテクチャでもアプリケーションは問題なく実行し、可能なときには G5 固有の命令と機能を利用できることが保証されます。 G5 で実行しているかどうかを判断するには、以下のルーチンを使用します。 このルーチンは、G5 固有のコードを実行できるかどうかのテストのみに使用すべきであり、ほかのハードウェア機能が存在するかどうかの判断に使用するべきではありません(この判断には、Gestalt や sysctl を使用できます)。 また、このルーチンは、速さに関しては最適化されていないため、最初に起動するときに、アプリケーションによって結果をキャッシュしておくようにします。
リスト 1. G5 のテスト |
#include <mach/mach.h> #include <mach/mach_host.h> #include <mach/host_info.h> #include <mach/machine.h> #ifndef CPU_SUBTYPE_POWERPC_970 #define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) #endif boolean_t IsG5() |
一般に、アーキテクチャの機能に関しては、可能な限り厳密であるのが良く、新しい機能/プロセッサを開発するときの互換性を向上する手助けになります。 これは、コードが特定の型のプロセッサに偏り過ぎるのを防ぐことになります。 アプリケーションの要件によっては、上記のルーチンを使う代わりに、sysctl または、gestaltPowerPCProcessorFeatures
Gestalt セレクタ(Gestalt.h を参照)に対応する新しいビットを使用して、必要な実際の機能をテストできます。 以下に、新しく定義されたビットを示します(これらは Mac OS X の以前のバージョンでは定義されていないため、上記の CPU_SUBTYPE_POWERPC_970
のように、条件付きで定義する必要があります)。
gestaltPowerPCHas64BitSupport = 6 | ダブルワードの LSU/ALU などを示します |
gestaltPowerPCHasDCBTStreams = 7 | 認識された DCBT の TH フィールドを示します |
gestaltPowerPCASArchitecture = 8 | PowerPC/AS のアーキテクチャを示します™ |
G5 向けの最適化に応用できる、一般的なパフォーマンス向上のテクニックが多数あり、さらに、G5 固有のテクニックもいくつかあります。 これらの技術は複雑で使いこなすのが難しいため、本稿ではそのほとんどを取り上げていません。 その代わり、「詳細情報」のセクションにあるその他のリソースを参照してください。 ここでは、G5 向けの最適化のための一般的なヒントとコツをいくつか簡単に紹介します。
このセクションで説明するテクニックを検討する前に、製品ビルドのコンパイラ設定をチェックして、コード中のプロセッサを多用する部分を、Project Builder の高度最適化(コンパイラ用の -O3
フラグ)を有効にした状態でコンパイルしていることを確認する必要があります。 このフラグは、プロセッサ固有の命令を生成するのではなく、プロセッサの使用率を軽減するための一般的なテクニックを使います。 このフラグを使うことでコードサイズが大きくなる(結果としてメモリ消費量が増える)可能性を考慮しても、プロセッサの使用率を軽減する価値があるかどうかを判断する必要があります。 さらに、以下に説明するほかのコンパイラフラグの中には、-O3
が有効になっている場合に限り十分な効果を発揮するものもあります。 考えられるもう 1 つの方法は、フラグの使用によって最大のパフォーマンスが得られる、ソースファイルごとにフラグを設定することです。 ソースファイルごとにフラグを追加するには、先述の「ソースファイルごとのフラグの設定」で説明したテクニックを使います。 ターゲット全体にフラグを設定するには、Project Builder の「ターゲットの設定」パネルに、次のようなポップアップメニューがあります。
![]() 図 11. ターゲット全体を対象に -O3 を有効にする |
G5 は、その浮動小数点レジスタと整数レジスタを通じて渡される大量のデータを処理するために最適化されています。 これらのレジスタを、間断なく常に新しいデータで満たすことができたときに、高いパフォーマンスが得られます。 こうしたことから、最適なパフォーマンスにとっての大きな障害の 1 つは、浮動小数点と整数の間の型変換であるといえます。 このような変換が行われると、G5 は停止し、あるレジスタセットから別のレジスタセットにデータを移し、その後処理を続行するということに時間をかけなければなりません。 変数を頻繁に変換するコードは、変換回数を減らし、なるべく浮動小数点や整数のままにできれば、パフォーマンスを改善できるはずです。
リスト 2. 浮動小数点−整数型間の型変換を最小限に抑える |
// この手動の "floor" コードは、2 つのパイプラインを // フラッシュさせるので、G5 では特に時間がかかる: float in, out; out = (float)((int)(in)); // instead, use floor() to keep everything in floating point registers: out = floor(in); |
G5 は、非常に効率的なメモリプリフェッチエンジンを備えており、実際にデータが必要になる前に、プロセッサに代わってメモリからデータを集め始めます。 G5 のこのプリフェッチエンジンは、互いのキャッシュライン(128 バイト)内の連続したメモリアドレスにアプリケーションがアクセスするのを検出すると、連続したメモリをさらにデータキャッシュにロードし始め、必要なときにすぐにメモリを利用できるようにしています。
したがって、このメモリプリフェッチエンジンを利用するには、メモリを順番に読めるように、アプリケーションのデータ構造が整理されているか、少なくともデータアクセスが整理されていることが重要です。 このように再編成することにより、速度は著しく向上します。
メモリを連続ブロックとして扱えるようにすると同時に、G5 は(以前のプロセッサよりもさらに)、多数の小さなメモリではなく、より大きな少数のメモリブロックのロード/割り当てがなされるときに処理が速くなります。 さまざまなテクニックを使用して、これを利用できます。 一般的な方法の 1 つは、グローバル変数の使用を減らし、ローカル変数の使用を増やして、小さなデータをメモリから頻繁にロードするのではなく、レジスタで処理するデータを増やすようにします。
多くのアプリケーションが、頻繁な行列演算、FFT、さらには基本的な線形代数演算など、集中的な計算処理を行っています。 残念ながら、こうしたアプリケーションの多くが、この処理を手動で行っており、コードのパフォーマンスを数倍高めるプロセッサ固有の機能をほとんど利用していません。 デベロッパの中には、1 つの Macintosh プロセッサまたはアーキテクチャに合わせて最適化したバージョンのルーチンを記述している方もいますが、結局、手動でチューンしたコードは G5 上では最適には実行しません。
アップルには VecLib という解決策があります。 VecLib は、Velocity Engine のほか、G3 から G4、G5 までの各プロセッサ向けに最適化された、一般的な数学計算処理ルーチンを多数提供しているフレームワークです。 VecLib の使用に切り替えることで、アプリケーションのコードは、個々のアーキテクチャに合わせてアップルの専門のエンジニアが慎重にチューニングしたルーチンを自動的に利用できるようになります。 ぜひお使いください。
メモリ内のコードのアラインメントは、G5 でパフォーマンスに影響する重要な要素です。 G5 は、一定の複数バイトの境界にアラインされたブロック内のメモリから命令を最適にフェッチします。 フェッチする位置がアラインされていない場合は、余分な作業が必要となります。 したがって、頻繁に使用するループ、関数、分岐、ラベルを、16 バイト(または 32 バイト)の境界に沿ってアラインすることにより、パフォーマンスを向上させることができます。 これを行うためのコンパイラフラグがあります。 Shark は、コードがアラインされていないループとその他のスポットを指摘します。 以下のフラグを使うと、コードのサイズが大きくなる場合があります。 これは、コンパイラによって、適切なアラインメントを保証するために no-ops がコードに挿入されたからです。 したがって、これらのフラグは、よく考えた上で、一般的に、ターゲット全体ではなく特定のソースファイルに対して使うべきです。 個別のソースファイルに対するフラグの追加は、先述の「ソースファイルごとのフラグの設定」で説明した方法で行うことができます。
-falign-loops=16 | 16 バイト境界に合わせてループのアラインメントを保証 |
-falign-functions=16 | 16 バイト境界に合わせて関数をアライン |
-falign-labels=16 | 16 バイト境界に合わせてラベルをアライン |
-falign-jumps=16 | 16 バイト境界に合わせてジャンプをアライン |
Shark によって LSU (Load/Store Unit) リジェクトが指摘されることがよくあります。 これは、G5 では極端なスローダウンにつながることがあり、あるアドレスからデータをロードしようとしているコードが、同じメモリアドレスにデータを格納しようとしているコードの後、あまりにもすぐに実行される場合に起こります。 これを行うとディパッチグループは拒否され、パイプラインフラッシュにつながることさえあります。 LSU リジェクトの一般的な原因としては、float-int 変換と、グローバル変数の使用が考えられます。 gcc の将来のバージョンには、LSU リジェクトを自動的に除去するためのより多くのサポートが追加される可能性があります。
状況によっては、以下に示すフラグを正しく使うことによって、実行時のパフォーマンスを改善できます。 正しく使用しないと、アプリケーションが予期しない影響を受ける可能性があります。 これらのフラグの使用にあたっては、事前に gcc の man ページ(コマンドラインから man gcc
)を参照して、その詳細を理解してください。
-ffast-math |
-funroll-loops |
-funroll-loops-all |
-finline |
-fobey-inline |
-finline-limit=N |
-malign-natural |
-mno-update |
-mno-multiple |
-fsched-interblock |
-fstrict-aliasing |
-mdynamic-no-pic |
-fprofile-arcs |
-freorder-blocks |
-freorder-blocks-and-partition |
-fbranch-probabilities |
TN2087 - G5 Performance Primer
2003 年 06 月 19 日版の GCC の man ページ。 この GCC はAugust 2003 gcc Updater に含まれています(Xcode Developer Preview によってもインストールされます)。